"""
因子计算器
=========

提供统一的因子计算接口和批量计算功能。
"""

import numpy as np
from typing import Dict, List, Optional, Union, Any, Callable
import time
import warnings
from concurrent.futures import ThreadPoolExecutor, as_completed
import threading

from ..alpha101.core import Alpha101Factory
from ..data.interface import DataInterface
from ..data.validator import DataValidator
from ..data.preprocessor import DataPreprocessor


class FactorCalculator:
    """
    因子计算器类
    
    提供高级的因子计算接口，支持批量计算、并行计算和结果缓存。
    """
    
    def __init__(self, use_numba: bool = True, max_workers: int = 4):
        """
        初始化因子计算器
        
        Parameters:
        -----------
        use_numba : bool
            是否使用Numba加速
        max_workers : int
            最大并行工作线程数
        """
        self.factory = Alpha101Factory(use_numba=use_numba)
        self.data_interface = DataInterface()
        self.validator = DataValidator()
        self.preprocessor = DataPreprocessor()
        
        self.max_workers = max_workers
        self.cache = {}
        self.cache_lock = threading.Lock()
        
        # 计算统计
        self.stats = {
            'total_calculations': 0,
            'cache_hits': 0,
            'cache_misses': 0,
            'total_time': 0.0,
            'average_time': 0.0
        }
    
    def compute_single_factor(self, factor_id: str, data: Dict[str, np.ndarray],
                             validate_data: bool = True,
                             preprocess_data: bool = True,
                             use_cache: bool = True,
                             **kwargs) -> Dict[str, Any]:
        """
        计算单个因子
        
        Parameters:
        -----------
        factor_id : str
            因子ID
        data : Dict[str, np.ndarray]
            输入数据
        validate_data : bool
            是否验证数据
        preprocess_data : bool
            是否预处理数据
        use_cache : bool
            是否使用缓存
        **kwargs
            额外参数
            
        Returns:
        --------
        Dict[str, Any]
            计算结果，包含因子值和元信息
        """
        start_time = time.time()
        
        try:
            # 生成缓存键
            cache_key = self._generate_cache_key(factor_id, data, kwargs) if use_cache else None
            
            # 检查缓存
            if use_cache and cache_key in self.cache:
                with self.cache_lock:
                    if cache_key in self.cache:
                        self.stats['cache_hits'] += 1
                        return self.cache[cache_key]
            
            self.stats['cache_misses'] += 1
            
            # 数据验证
            validation_report = None
            if validate_data:
                validation_report = self.validator.validate_all(data, strict=False)
                if not validation_report['is_valid']:
                    raise ValueError(f"Data validation failed: {validation_report['errors']}")
            
            # 数据预处理
            processed_data = data
            preprocessing_report = None
            if preprocess_data:
                processed_data = self.preprocessor.preprocess_all(data)
                preprocessing_report = self.preprocessor.get_preprocessing_report(data, processed_data)
            
            # 计算因子
            factor_values = self.factory.compute_factor(factor_id, processed_data, **kwargs)
            
            # 构建结果
            result = {
                'factor_id': factor_id,
                'factor_values': factor_values,
                'computation_time': time.time() - start_time,
                'data_shape': next(iter(data.values())).shape,
                'validation_report': validation_report,
                'preprocessing_report': preprocessing_report,
                'config': self.factory.get_factor_info(factor_id),
                'cache_used': False
            }
            
            # 缓存结果
            if use_cache and cache_key:
                with self.cache_lock:
                    self.cache[cache_key] = result.copy()
                    result['cache_used'] = True
            
            # 更新统计
            self.stats['total_calculations'] += 1
            self.stats['total_time'] += result['computation_time']
            self.stats['average_time'] = self.stats['total_time'] / self.stats['total_calculations']
            
            return result
            
        except Exception as e:
            return {
                'factor_id': factor_id,
                'error': str(e),
                'computation_time': time.time() - start_time,
                'success': False
            }
    
    def compute_multiple_factors(self, factor_ids: List[str], 
                               data: Dict[str, np.ndarray],
                               parallel: bool = True,
                               validate_data: bool = True,
                               preprocess_data: bool = True,
                               use_cache: bool = True,
                               **kwargs) -> Dict[str, Dict[str, Any]]:
        """
        计算多个因子
        
        Parameters:
        -----------
        factor_ids : List[str]
            因子ID列表
        data : Dict[str, np.ndarray]
            输入数据
        parallel : bool
            是否并行计算
        validate_data : bool
            是否验证数据
        preprocess_data : bool
            是否预处理数据
        use_cache : bool
            是否使用缓存
        **kwargs
            额外参数
            
        Returns:
        --------
        Dict[str, Dict[str, Any]]
            各因子的计算结果
        """
        if not parallel or len(factor_ids) == 1:
            # 串行计算
            results = {}
            for factor_id in factor_ids:
                results[factor_id] = self.compute_single_factor(
                    factor_id, data, validate_data, preprocess_data, use_cache, **kwargs
                )
            return results
        
        # 并行计算
        results = {}
        with ThreadPoolExecutor(max_workers=self.max_workers) as executor:
            # 提交任务
            future_to_factor = {
                executor.submit(
                    self.compute_single_factor,
                    factor_id, data, validate_data, preprocess_data, use_cache, **kwargs
                ): factor_id
                for factor_id in factor_ids
            }
            
            # 收集结果
            for future in as_completed(future_to_factor):
                factor_id = future_to_factor[future]
                try:
                    results[factor_id] = future.result()
                except Exception as e:
                    results[factor_id] = {
                        'factor_id': factor_id,
                        'error': str(e),
                        'success': False
                    }
        
        return results
    
    def compute_factor_matrix(self, factor_ids: List[str],
                            data: Dict[str, np.ndarray],
                            **kwargs) -> Dict[str, Any]:
        """
        计算因子矩阵（所有因子的值组合成一个3D数组）
        
        Parameters:
        -----------
        factor_ids : List[str]
            因子ID列表
        data : Dict[str, np.ndarray]
            输入数据
        **kwargs
            额外参数
            
        Returns:
        --------
        Dict[str, Any]
            因子矩阵和相关信息
        """
        # 计算所有因子
        results = self.compute_multiple_factors(factor_ids, data, **kwargs)
        
        # 提取成功计算的因子
        successful_factors = []
        factor_values_list = []
        
        for factor_id in factor_ids:
            result = results[factor_id]
            if 'factor_values' in result and result['factor_values'] is not None:
                successful_factors.append(factor_id)
                factor_values_list.append(result['factor_values'])
        
        if not factor_values_list:
            return {
                'factor_matrix': None,
                'factor_ids': [],
                'error': 'No factors computed successfully'
            }
        
        # 构建因子矩阵 (n_factors, n_stocks, n_days)
        factor_matrix = np.stack(factor_values_list, axis=0)
        
        return {
            'factor_matrix': factor_matrix,
            'factor_ids': successful_factors,
            'shape': factor_matrix.shape,
            'successful_count': len(successful_factors),
            'failed_count': len(factor_ids) - len(successful_factors),
            'individual_results': results
        }
    
    def benchmark_factor(self, factor_id: str, data: Dict[str, np.ndarray],
                        n_runs: int = 5, **kwargs) -> Dict[str, Any]:
        """
        因子性能基准测试
        
        Parameters:
        -----------
        factor_id : str
            因子ID
        data : Dict[str, np.ndarray]
            输入数据
        n_runs : int
            运行次数
        **kwargs
            额外参数
            
        Returns:
        --------
        Dict[str, Any]
            基准测试结果
        """
        times = []
        results = []
        
        for i in range(n_runs):
            result = self.compute_single_factor(
                factor_id, data, use_cache=False, **kwargs
            )
            
            if 'computation_time' in result:
                times.append(result['computation_time'])
                results.append(result)
        
        if not times:
            return {'error': 'No successful runs'}
        
        return {
            'factor_id': factor_id,
            'n_runs': n_runs,
            'times': times,
            'mean_time': np.mean(times),
            'std_time': np.std(times),
            'min_time': np.min(times),
            'max_time': np.max(times),
            'data_shape': next(iter(data.values())).shape,
            'throughput_per_second': 1.0 / np.mean(times),
            'results': results
        }
    
    def validate_factor_result(self, factor_id: str, result: np.ndarray,
                             data: Dict[str, np.ndarray]) -> Dict[str, Any]:
        """
        验证因子计算结果
        
        Parameters:
        -----------
        factor_id : str
            因子ID
        result : np.ndarray
            因子计算结果
        data : Dict[str, np.ndarray]
            输入数据
            
        Returns:
        --------
        Dict[str, Any]
            验证结果
        """
        validation = {
            'is_valid': True,
            'issues': [],
            'statistics': {}
        }
        
        # 基本形状检查
        expected_shape = next(iter(data.values())).shape
        if result.shape != expected_shape:
            validation['is_valid'] = False
            validation['issues'].append(f"Shape mismatch: expected {expected_shape}, got {result.shape}")
        
        # 数值检查
        if np.all(np.isnan(result)):
            validation['is_valid'] = False
            validation['issues'].append("All values are NaN")
        
        if np.any(np.isinf(result)):
            validation['is_valid'] = False
            validation['issues'].append("Contains infinite values")
        
        # 统计信息
        valid_mask = ~np.isnan(result)
        if np.any(valid_mask):
            valid_values = result[valid_mask]
            validation['statistics'] = {
                'valid_ratio': np.sum(valid_mask) / result.size,
                'mean': np.mean(valid_values),
                'std': np.std(valid_values),
                'min': np.min(valid_values),
                'max': np.max(valid_values),
                'percentiles': {
                    '1%': np.percentile(valid_values, 1),
                    '5%': np.percentile(valid_values, 5),
                    '25%': np.percentile(valid_values, 25),
                    '50%': np.percentile(valid_values, 50),
                    '75%': np.percentile(valid_values, 75),
                    '95%': np.percentile(valid_values, 95),
                    '99%': np.percentile(valid_values, 99)
                }
            }
        
        return validation
    
    def _generate_cache_key(self, factor_id: str, data: Dict[str, np.ndarray],
                           kwargs: Dict[str, Any]) -> str:
        """生成缓存键"""
        # 简化的缓存键生成（实际应用中可能需要更复杂的哈希）
        data_hash = hash(tuple(
            (k, v.shape, v.dtype, hash(v.tobytes()) if v.size < 10000 else hash(str(v.shape)))
            for k, v in sorted(data.items())
        ))
        kwargs_hash = hash(tuple(sorted(kwargs.items())))
        
        return f"{factor_id}_{data_hash}_{kwargs_hash}"
    
    def clear_cache(self):
        """清空缓存"""
        with self.cache_lock:
            self.cache.clear()
    
    def get_cache_info(self) -> Dict[str, Any]:
        """获取缓存信息"""
        with self.cache_lock:
            return {
                'cache_size': len(self.cache),
                'cache_hits': self.stats['cache_hits'],
                'cache_misses': self.stats['cache_misses'],
                'hit_ratio': (self.stats['cache_hits'] / 
                            (self.stats['cache_hits'] + self.stats['cache_misses'])
                            if (self.stats['cache_hits'] + self.stats['cache_misses']) > 0 else 0)
            }
    
    def get_computation_stats(self) -> Dict[str, Any]:
        """获取计算统计信息"""
        return self.stats.copy()
    
    def reset_stats(self):
        """重置统计信息"""
        self.stats = {
            'total_calculations': 0,
            'cache_hits': 0,
            'cache_misses': 0,
            'total_time': 0.0,
            'average_time': 0.0
        }
    
    def list_available_factors(self) -> List[str]:
        """列出所有可用因子"""
        return self.factory.list_available_factors()
    
    def get_factor_info(self, factor_id: str) -> Dict[str, Any]:
        """获取因子信息"""
        return self.factory.get_factor_info(factor_id)
    
    def create_sample_data(self, n_stocks: int = 100, n_days: int = 252,
                          seed: int = 42) -> Dict[str, np.ndarray]:
        """创建示例数据"""
        return self.data_interface.create_sample_data(n_stocks, n_days, seed)